use std::{array::from_fn, fs::write};

use crate::{
    class::StyleCommand,
    custom_widget::{collapse_grid, collapse_grid_event},
    loader::GameSource,
    ui::game_map::{
        ui_block_class, ui_block_clear_class, ui_four_block_class, ui_game_map, Block, FourBlock,
        GameMapSettings,
    },
    ui_class::{despawn_screen, left_panel},
    widget::{atlas_image, image, node_children, node_root, node_text},
    GameMaps, GameState, OnExitClean,
};
use bevy::prelude::*;
use serde_ron::to_string;

pub struct MapEditorPlugin;

impl Plugin for MapEditorPlugin {
    fn build(&self, app: &mut App) {
        app.add_systems(
            OnEnter(GameState::MapEditor),
            (setup_map_editor, ui_game_map),
        )
        .add_systems(
            Update,
            (collapse_grid_event, block_event, map_brush).run_if(in_state(GameState::MapEditor)),
        )
        .add_systems(OnExit(GameState::MapEditor), despawn_screen::<OnExitClean>);
    }
}

fn setup_map_editor(mut commands: Commands, map: Res<GameMaps>) {
    node_root((), commands.reborrow(), OnExitClean, |gc| {
        node_children(left_panel, gc, (), |gc| {
            collapse_grid(map.maps.len(), 1, 30., "GAME LEVEL", gc, |gc, r, _| {
                node_children(
                    (collapse_col, selector_border),
                    gc,
                    (Interaction::default(),),
                    |gc| {
                        image(map_img, gc, ());
                        node_text(map.maps[r].name.clone(), map_text, gc, ());
                    },
                );
            });
            collapse_grid(5, 2, 75., "GAME BLOCK", gc, |gc, r, c| {
                ui_block_wall(gc, r, c);
            });
        });
    });
}
#[derive(Component)]
pub struct BlockType(usize, usize, usize);
#[derive(Component)]
pub struct FourBlockType(usize);

#[derive(Component)]
pub struct RelateBlockWall1([[usize; 2]; 2]);

fn ui_block_wall(commands: &mut ChildBuilder, r: usize, c: usize) {
    let index = r * 2 + c;
    if index < 6 {
        node_children(
            block_wall_class,
            commands,
            (Interaction::None, RelateBlockWall1([[index; 2]; 2])),
            |gc| {
                if [1, 2].contains(&index) {
                    let _a: [Entity; 4] = from_fn(|i| {
                        image(
                            block_wall_item_class,
                            gc,
                            (Interaction::None, BlockType(r, c, i)),
                        )
                    });
                } else {
                    let _a: [Entity; 4] =
                        from_fn(|i| image(block_wall_item_class, gc, BlockType(r, c, i)));
                }
            },
        );
    } else if index < 9 {
        node_children(
            block_wall_class,
            commands,
            (Interaction::None, RelateBlockWall1([[index; 2]; 2])),
            |gc| {
                atlas_image(home_class, gc, FourBlockType(index));
            },
        );
    }
}

fn home_class(
    mut style: Mut<Style>,
    mut image: Mut<UiImage>,
    mut layout: Mut<TextureAtlas>,
    gm_res: Res<GameSource>,
    block_type: Mut<FourBlockType>,
) {
    style.width = Val::Percent(100.);
    style.height = Val::Percent(100.);
    image.texture = gm_res.blocks[block_type.0 - 1].clone();
    if block_type.0 == 6 {
        layout.layout = gm_res.tank_home.clone();
    } else {
        layout.layout = gm_res.tank_layout.clone();
    }
}

fn block_wall_class(mut style: Mut<Style>) {
    style.display = Display::Flex;
    style.flex_wrap = FlexWrap::Wrap;
    style.width = Val::Px(30.);
    style.height = Val::Px(30.);
    style.border = UiRect::all(Val::Px(2.));
}

fn block_wall_item_class(
    mut style: Mut<Style>,
    mut image: Mut<UiImage>,
    block_type: Mut<BlockType>,
    gm_res: Res<GameSource>,
    mut background: Mut<BackgroundColor>,
) {
    style.width = Val::Percent(50.);
    style.height = Val::Percent(50.);
    if block_type.0 + block_type.1 > 0 {
        image.texture = gm_res.blocks[block_type.0 * 2 + block_type.1 - 1].clone();
    } else {
        background.0 = Color::BLACK;
    }
}

fn selector_border(mut style: Mut<Style>) {
    style.border = UiRect::all(Val::Px(2.));
}

fn collapse_col(mut style: Mut<Style>) {
    style.display = Display::Flex;
    style.align_items = AlignItems::Center;
    style.width = Val::Percent(100.);
    style.height = Val::Percent(100.);
    style.overflow = Overflow::clip();
}

fn map_img(mut style: Mut<Style>, mut image: Mut<UiImage>, gm_res: Res<GameSource>) {
    style.height = Val::Px(24.);
    style.width = Val::Px(24.);
    style.margin.right = Val::Px(50.);
    style.margin.left = Val::Px(10.);
    image.texture = gm_res.game_map.clone();
}

fn map_text(mut text: Mut<Text>, gm_res: Res<GameSource>) {
    text.sections[0].style.font_size = 20.;
    text.sections[0].style.color = Color::BLACK;
    text.sections[0].style.font = gm_res.font_mono.clone();
}

fn block_event(
    mut query: Query<
        (&Interaction, Entity, &Children),
        (Changed<Interaction>, With<RelateBlockWall1>),
    >,
    mut query_border: Query<(&mut BorderColor, &mut RelateBlockWall1)>,
    mut query_block: Query<
        (
            &Interaction,
            &BlockType,
            &mut BackgroundColor,
            &mut UiImage,
            &Parent,
        ),
        Changed<Interaction>,
    >,
    gm_res: Res<GameSource>,
    mut settings: ResMut<GameMapSettings>,
) {
    for (interaction, block_type, mut background, mut image, parent) in query_block.iter_mut() {
        if let Ok((border, mut relate)) = query_border.get_mut(parent.get()) {
            if *interaction == Interaction::Pressed && border.0 == Color::RED {
                let r: usize = if block_type.2 > 1 { 1 } else { 0 };
                let c = block_type.2 % 2;
                if background.0 == Color::BLACK {
                    background.0 = Color::WHITE;
                    image.texture = gm_res.blocks[block_type.0 * 2 + block_type.1 - 1].clone();
                    relate.0[r][c] = block_type.0 * 2 + block_type.1;
                } else {
                    background.0 = Color::BLACK;
                    image.texture = Default::default();
                    relate.0[r][c] = 0;
                }
            }
        }
    }
    for (interaction, entity, _children) in query.iter_mut() {
        if *interaction == Interaction::Pressed {
            query_border.iter_mut().for_each(|(mut border, _)| {
                border.0 = Color::NONE;
            });
            if let Ok((mut border, relate)) = query_border.get_mut(entity) {
                border.0 = Color::RED;
                settings.block_select = relate.0;
            }
        }
    }
}

fn map_brush(
    mut commands: Commands,
    mut query_blocks: Query<(Entity, &mut Block)>,
    mut query_four_blocks: Query<(Entity, &mut FourBlock)>,
    mut gizmos: Gizmos,
    mut move_event: EventReader<CursorMoved>,
    mouse_button_input: Res<ButtonInput<MouseButton>>,
    enter_event: Res<ButtonInput<KeyCode>>,
    mut settings: ResMut<GameMapSettings>,
    mut game_map: ResMut<GameMaps>,
) {
    for evt in move_event.read() {
        settings.map_cursor_pos = evt.position - settings.physics_center + settings.map_center;
        settings.map_cursor_pos.y = -settings.map_cursor_pos.y;
        if settings.map_cursor_pos.x >= -312.
            && settings.map_cursor_pos.y >= -312.
            && settings.map_cursor_pos.x < 312.
            && settings.map_cursor_pos.y < 312.
        {
            settings.map_cursor_block = (
                (-settings.map_cursor_pos.y / 48. + 6.5) as usize,
                (settings.map_cursor_pos.x / 48. + 6.5) as usize,
            );
            settings.show_line = true;
        } else {
            settings.show_line = false;
        }
    }
    if settings.show_line {
        let x_start = settings.map_cursor_block.1 as f32 * 48. - 312.;
        let x_end = settings.map_cursor_block.1 as f32 * 48. - 312. + 48.;
        let x_start_edge = settings.map_center.x - 312.;
        let x_end_edge = settings.map_center.x + 312.;
        let y_start = -(settings.map_cursor_block.0 as f32) * 48. + 312.;
        let y_end = -(settings.map_cursor_block.0 as f32) * 48. + 264.;
        let y_start_edge = settings.map_center.y - 312.;
        let y_end_edge = settings.map_center.y + 312.;
        gizmos.line_2d(
            Vec2::new(x_start, y_start_edge),
            Vec2::new(x_start, y_end_edge),
            Color::RED,
        );
        gizmos.line_2d(
            Vec2::new(x_end, y_start_edge),
            Vec2::new(x_end, y_end_edge),
            Color::RED,
        );
        gizmos.line_2d(
            Vec2::new(x_start_edge, y_start),
            Vec2::new(x_end_edge, y_start),
            Color::RED,
        );
        gizmos.line_2d(
            Vec2::new(x_start_edge, y_end),
            Vec2::new(x_end_edge, y_end),
            Color::RED,
        );
        if mouse_button_input.pressed(MouseButton::Left) {
            query_blocks.iter_mut().for_each(|(entity, mut block)| {
                if block.0 >= settings.map_cursor_block.0 * 2
                    && block.1 >= settings.map_cursor_block.1 * 2
                    && block.0 < settings.map_cursor_block.0 * 2 + 2
                    && block.1 < settings.map_cursor_block.1 * 2 + 2
                {
                    block.2 = settings.block_select[block.0 - settings.map_cursor_block.0 * 2]
                        [block.1 - settings.map_cursor_block.1 * 2];
                    game_map.maps[settings.level_select].map[block.0][block.1] = block.2;
                    query_four_blocks
                        .iter_mut()
                        .for_each(|(four_entity, mut four_block)| {
                            if block.2 < 6 {
                                commands.set_style(entity, ui_block_class);
                                if four_block.0 == block.0 && four_block.1 == block.1 {
                                    commands.set_style(four_entity, ui_block_clear_class);
                                }
                            } else {
                                commands.set_style(entity, ui_block_clear_class);
                                if four_block.2 == settings.block_select[0][0] {
                                    if four_block.0 != 99 {
                                        game_map.maps[settings.level_select].map[four_block.0]
                                            [four_block.1] = 0;
                                        game_map.maps[settings.level_select].map[four_block.0]
                                            [four_block.1 + 1] = 0;
                                        game_map.maps[settings.level_select].map
                                            [four_block.0 + 1][four_block.1] = 0;
                                        game_map.maps[settings.level_select].map
                                            [four_block.0 + 1][four_block.1 + 1] = 0;
                                    }
                                    four_block.0 = settings.map_cursor_block.0 * 2;
                                    four_block.1 = settings.map_cursor_block.1 * 2;
                                    game_map.maps[settings.level_select].map[four_block.0]
                                        [four_block.1] = four_block.2;
                                    game_map.maps[settings.level_select].map[four_block.0]
                                        [four_block.1 + 1] = four_block.2;
                                    game_map.maps[settings.level_select].map[four_block.0 + 1]
                                        [four_block.1] = four_block.2;
                                    game_map.maps[settings.level_select].map[four_block.0 + 1]
                                        [four_block.1 + 1] = four_block.2;
                                    commands.set_style(four_entity, ui_four_block_class);
                                }
                            }
                        });
                }
            });
        }
    }
    if enter_event.just_pressed(KeyCode::Enter) {
        if let Ok(serilize) = to_string(game_map.as_ref()) {
            write("assets/map.ron", serilize).expect("unable to write file");
            println!("write file success!");
        }
    }
}
